package com.kisyou.事务;

import com.mysql.cj.jdbc.MysqlDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;

/**
 * 使用事务
 * 1. 直接在 SQL 中使用
 *   begin/start transaction; 开始
 *   执行 SQL
 *   Commit 或者 rollback 结尾
 * 2. JDBC 中使用，利用 Connection 有个 auto-commit(默认是 true），每次执行 SQL，自动 commit。
 * 关闭 auto-commit，把这个属性改成 false(不关闭的话，每次执行SQL，都会自动提交)
 *       执行完 SQL 之后，手动去 commit / rollback
 */

/**
 * 不同隔离级别可能引起的问题：
 * 隔离级别     存在问题                解释
 *
 * 读未提交     脏读现象                看到了脏数据（临时的数据，不知道最后会不会生效）
 *             不可重复读现象
 *             幻读现象
 *
 * 读已提交     没有了脏读现象           同一个事务期间，多次 select 一条记录，可能出现结果不同（被别的已经提交的事务修改过）
 *             不可重复读现象
 *             幻读现象
 *
 * 可重复读     没有了不可重复读现象      同一个事务期间，修改全量数据时，可能出现未被修改的幽灵记录（被别的已经提交的事务插入的）
 *             幻读现象（phantom read）
 *             MySQL 目前的实现中，没有幻读（间隙锁搞定）
 *
 * 可串行化
 */
public class WorkInJDBC {
    public static void main(String[] args) throws SQLException {
        MysqlDataSource mysqlDataSource = new MysqlDataSource();
        mysqlDataSource.setUser("debian-sys-maint");
        mysqlDataSource.setPassword("co7lI761pHOFy9gx");
        mysqlDataSource.setUrl("jdbc:mysql://127.0.0.1:3307/jdbc_mysql?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai");

        Scanner scanner = new Scanner(System.in);

        try (Connection c = mysqlDataSource.getConnection()) {
            // 1) 关闭 auto-commit
            c.setAutoCommit(false);

            executeSql(c, "update accounts set money = money - 50 where name = '张三'");

            scanner.nextLine(); // 停下来等故障

            executeSql(c, "update accounts set money = money + 50 where name = '李四'");

            // 2) 手动 commit
            c.commit();
        }
    }

    private static void executeSql(Connection c, String sql) throws SQLException {
        try (PreparedStatement ps = c.prepareStatement(sql)) {
            ps.executeUpdate();
        }
    }
}
